﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Printing;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Documents.Serialization;
using System.Windows.Xps;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;
using PersonaLibrary.ViewModel.PrintViewModel.Model;

namespace PersonaLibrary.ViewModel.PrintViewModel
{
	internal class PrintVM : BaseViewModel<View.PrintView.PrintPreview>
	{
		private IDocumentPaginatorSource _printingContent;
		private IBasePrintTemplate _selectedTemplate;
		private PrintQueue _seletedPrinter;
		private PageMediaSize _selectedSize;
		private PageThiknessModel _selectedThikness;
		private KeyValuePair<string, OrientationModel> _selectedOrienatation;
		private string _customPageIndexs;
		private KeyValuePair<string, PrintPagesCount> _selectedPagesCount;
		private int _copies = 1;

		private PageRange? _range;

		private readonly Regex _customPagesPattern = new Regex(@"(^\d+-\d+$)");
		private readonly Regex _singlePagePattern = new Regex(@"^\d+$");

		private RelayCommand _printCommand;
		private RelayCommand _refreshView;

		public RelayCommand RefreshView
		{
			get
			{
				if (_refreshView == null)
					_refreshView = new RelayCommand(x => RefreshPages(), x => SelectedTemplate != null);
				return _refreshView;
			}
		}

		public RelayCommand ShowTemplateProperties
		{
			get
			{
				ITemplateConfigurable config = _selectedTemplate as ITemplateConfigurable;
				if (config != null)
					return config.ShowConfig;
				return null;
			}
		}

		public RelayCommand ShowTemplateConflicts
		{
			get
			{
				IAvailableConflict conflict = _selectedTemplate as IAvailableConflict;
				if (conflict != null)
					return conflict.ShowConflict;
				return null;
			}
		}

		public RelayCommand ShowOrder
		{
			get
			{
				IOrder order = _selectedTemplate as IOrder;
				if (order != null)
					return order.ShowOrder;
				return null;
			}
		}

		public RelayCommand PrintCommand
		{
			get
			{
				if (_printCommand == null)
					_printCommand = new RelayCommand
						(
							x => Print(),
							x => (_seletedPrinter != null) && (_selectedSize != null) && (_selectedTemplate != null)
						);
				return _printCommand;
			}
		}

		public PrintVM(IEnumerable<Data.Employee> items)
		{
			LocalPrintServer printServer = new LocalPrintServer();
			Printers = printServer.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections });

			Templates = new List<IBasePrintTemplate>
							{
								new BithdayTemplate { Items = items.OrderBy(x => x.MultiBirthday).ToList(), TemplateName = "Дни рождения" },
								new StickersTemplate{Items = items.ToList(), TemplateName = "Стикеры"}
							};

			if (items.Count() == 1)
				Templates.Insert(0, new SinglePrintTemplate { Items = items.ToList(), TemplateName = "Простой шаблон" });

			Orientations = new Dictionary<string, OrientationModel>(2)
							   {
								   {"Портрет", new OrientationModel("../../Images/Portrait.png", PageOrientation.Portrait)},
								   {"Ландшафт", new OrientationModel("../../Images/Landscape.png", PageOrientation.Landscape)}
							   };
			PagesCount = new Dictionary<string, PrintPagesCount>(3)
							 {
								 {"Все", PrintPagesCount.All},
								 {"Номера", PrintPagesCount.Custom}
							 };

			View.Loaded += delegate
			{
				SelectedPrinter =
					Printers.FirstOrDefault(x => x.FullName == printServer.DefaultPrintQueue.FullName);
				SelectedSize =
					PrinterCapabilities.PageMediaSizeCapability.FirstOrDefault(x =>
						x.PageMediaSizeName == printServer.DefaultPrintQueue.DefaultPrintTicket.PageMediaSize.PageMediaSizeName);
				SelectedOrientation =
					Orientations.FirstOrDefault(
						x => x.Value.Orientation == printServer.DefaultPrintQueue.DefaultPrintTicket.PageOrientation);
				SelectedThikness =
					AvalableThikness.FirstOrDefault();
			};

			SelectedPagesCount = PagesCount.FirstOrDefault();
		}

		public List<IBasePrintTemplate> Templates { private set; get; }

		public RoutedEventHandler ViewLoaded { private set; get; }

		public PrintQueueCollection Printers { private set; get; }

		public Dictionary<string, OrientationModel> Orientations { private set; get; }

		public Dictionary<string, PrintPagesCount> PagesCount { private set; get; }

		public PrintCapabilities PrinterCapabilities { private set; get; }

		public List<PageThiknessModel> AvalableThikness { private set; get; }

		public IDocumentPaginatorSource PrintingContent
		{
			get { return _printingContent; }
			set
			{
				_printingContent = value;
				RisePropertyChanged("PrintingContent");
			}
		}

		public IBasePrintTemplate SelectedTemplate
		{
			set
			{
				if (SelectedTemplate != null)
					_selectedTemplate.UnLoad();
				_selectedTemplate = value;
				OnSelectedTemplateChanged();
			}
			get
			{
				return _selectedTemplate;
			}
		}

		public PrintQueue SelectedPrinter
		{
			set
			{
				_seletedPrinter = value;
				OnSelectedPrinterChanged();
			}
			get
			{
				return _seletedPrinter;
			}
		}

		public PageMediaSize SelectedSize
		{
			get
			{
				return _selectedSize;
			}
			set
			{
				_selectedSize = value;
				OnSelectedSizeChanged();
			}
		}

		public PageThiknessModel SelectedThikness
		{
			get
			{
				return _selectedThikness;
			}
			set
			{
				_selectedThikness = value;
				OnSelectedThiknessChanged();
			}
		}

		public int Copies
		{
			get
			{
				return _copies;
			}
			set
			{
				_copies = value;
				RisePropertyChanged("Copies");
			}
		}

		public bool IsConfiguralable
		{
			get { return _selectedTemplate is ITemplateConfigurable; }
		}

		public KeyValuePair<string, OrientationModel> SelectedOrientation
		{
			set
			{
				_selectedOrienatation = value;
				OnOrientationChange();
			}
			get
			{
				return _selectedOrienatation;
			}
		}

		public KeyValuePair<string, PrintPagesCount> SelectedPagesCount
		{
			set
			{
				_selectedPagesCount = value;
				OnPagesCountChange();
			}
			get
			{
				return _selectedPagesCount;
			}
		}

		public string CustomPageIndexs
		{
			set
			{
				_customPageIndexs = value;
				OnCustomPageIndexs();
			}
			get
			{
				return _customPageIndexs;
			}
		}

		private void Print()
		{
			SelectedPrinter.UserPrintTicket.CopyCount = Copies;
			SelectedPrinter.UserPrintTicket.PageOrientation = SelectedOrientation.Value.Orientation;
			SelectedPrinter.UserPrintTicket.PageMediaSize = _selectedSize;

			if (_selectedPagesCount.Value == PrintPagesCount.Custom && _range != null)
			{
				XpsDocumentWriter doc = PrintQueue.CreateXpsDocumentWriter(SelectedPrinter);
				DocumentPaginatorWrapper wrapper = new DocumentPaginatorWrapper(SelectedTemplate.Document.DocumentPaginator)
				{
					Range = _range
				};
				doc.Write(wrapper);
			}
			else
			{
				XpsDocumentWriter doc = PrintQueue.CreateXpsDocumentWriter(SelectedPrinter);
				doc.Write(SelectedTemplate.Document.DocumentPaginator);
			}
		}

		private void OnCustomPageIndexs()
		{
			if ((_customPagesPattern.IsMatch(_customPageIndexs) || _singlePagePattern.IsMatch(_customPageIndexs)) && SelectedTemplate != null)
			{
				SelectedPagesCount = PagesCount.FirstOrDefault(x => x.Value == PrintPagesCount.Custom);
				string[] result = _customPageIndexs.Split("-".ToCharArray());
				if (result.Length == 1)
				{
					int parseResult;
					if (int.TryParse(_customPageIndexs, out parseResult))
					{
						if (parseResult <= SelectedTemplate.Document.DocumentPaginator.PageCount)
							_range = new PageRange(parseResult);
						else
						{
							SelectedPagesCount = PagesCount.FirstOrDefault();
							_range = null;
						}
					}
				}
				else
				{
					if (result.Length == 2)
					{
						int startNumber;
						int endNumber;
						if (int.TryParse(result[0], out startNumber) && int.TryParse(result[1], out endNumber))
						{
							if (startNumber <= SelectedTemplate.Document.DocumentPaginator.PageCount && endNumber <= SelectedTemplate.Document.DocumentPaginator.PageCount)
								_range = new PageRange(startNumber, endNumber);
							else
							{
								SelectedPagesCount = PagesCount.FirstOrDefault();
								_range = null;
							}
						}
					}
				}

			}
			else
			{
				SelectedPagesCount = PagesCount.FirstOrDefault();
				_range = null;
			}

			RisePropertyChanged("CustomPageIndexs");
			RisePropertyChanged("IsCurrentPagePrint");
		}

		private void OnSelectedTemplateChanged()
		{
			Stopwatch timer = Stopwatch.StartNew();
			RefreshPages();
			timer.Stop();
			Debug.WriteLine(timer.Elapsed);
			RisePropertyChanged("SelectedTemplate");
			RisePropertyChanged("ShowTemplateProperties");
			RisePropertyChanged("ShowTemplateConflicts");
			RisePropertyChanged("ShowOrder");
		}

		private void OnSelectedPrinterChanged()
		{
			PrinterCapabilities = SelectedPrinter.GetPrintCapabilities();
			RisePropertyChanged("SelectedPrinter");
			RisePropertyChanged("PrinterCapabilities");
		}

		private void OnSelectedSizeChanged()
		{
			if (PrinterCapabilities == null)
			{
				RisePropertyChanged("SelectedSize");
				return;
			}
			List<PageThiknessModel> result = new List<PageThiknessModel>
										 {
											 new PageThiknessModel("../../Images/MidPageMargin.png", CreateThickness(ToCm(2.5), ToCm(1.5), ToCm(1.5), ToCm(1.5))),
											 new PageThiknessModel("../../Images/MaxPageMargin.png", CreateThickness(ToCm(3.5), ToCm(2.5), ToCm(2.5), ToCm(2.5))),
											 new PageThiknessModel("../../Images/LitlePageMargin.png",
												 CreateThickness(_selectedSize.Width.Value - PrinterCapabilities.PageImageableArea.ExtentWidth, 
												 _selectedSize.Height.Value - PrinterCapabilities.PageImageableArea.ExtentHeight,
												 _selectedSize.Width.Value - PrinterCapabilities.PageImageableArea.ExtentWidth, 
												 _selectedSize.Height.Value - PrinterCapabilities.PageImageableArea.ExtentHeight))
										 };
			AvalableThikness = result;
			RisePropertyChanged("AvalableThikness");
			RisePropertyChanged("SelectedSize");
			RefreshPages();
		}

		private Thickness CreateThickness(double left, double top, double right, double bottom)
		{
			if (SelectedOrientation.Value != null)
			{
				if (SelectedOrientation.Value.Orientation == PageOrientation.Portrait)
					return new Thickness(left, top, right, bottom);

				if (SelectedOrientation.Value.Orientation == PageOrientation.Landscape)
					return new Thickness(bottom, left, top, right);
			}
			return new Thickness(left, top, right, bottom);
		}

		private void OnSelectedThiknessChanged()
		{
			RisePropertyChanged("SelectedThikness");
			RefreshPages();
		}

		private void OnOrientationChange()
		{
			SelectedPrinter.UserPrintTicket.PageOrientation = _selectedOrienatation.Value.Orientation;

			RefreshPages();

			RisePropertyChanged("SelectedOrientation");
		}

		private void RefreshPages()
		{
			if (SelectedTemplate != null)
			{

				if (_selectedThikness == null)
					SelectedThikness = AvalableThikness[0];

				if (SelectedOrientation.Value.Orientation == PageOrientation.Portrait)
				{
					SelectedTemplate.Refresh(new Size(SelectedSize.Width.Value, SelectedSize.Height.Value),
											 SelectedThikness.Thickness);
				}

				if (SelectedOrientation.Value.Orientation == PageOrientation.Landscape)
				{
					SelectedTemplate.Refresh(new Size(SelectedSize.Height.Value, SelectedSize.Width.Value),
											 SelectedThikness.Thickness);
				}
			}
		}

		private void OnPagesCountChange()
		{
			RisePropertyChanged("SelectedPagesCount");
		}

		private static double ToCm(double pix)
		{
			return pix * (96 / 2.54);
		}

		public override void Dispose()
		{
			base.Dispose();
			Printers.Dispose();
			if (SelectedTemplate != null)
				SelectedTemplate.UnLoad();
		}
	}
}
